using System;
using System.Management.Automation;
using System.IO;

namespace PSSymbolStore.Commands
{
    [Cmdlet(VerbsCommon.Add, "Symbols", SupportsShouldProcess = true)]
    public class AddSymbolsCommand : SymbolStoreCommandBase
    {
        [Parameter(Position = 1,
            Mandatory = false,
            ValueFromPipeline = false,
            ValueFromPipelineByPropertyName = true,
            ValueFromRemainingArguments = false,
            HelpMessage = "Store pointers to files instead of uploading the actual files")]
        [ValidateNotNullOrEmpty]
        public SwitchParameter Pointers { get; set; }

        [Parameter(Position = 2,
            Mandatory = false,
            ValueFromPipeline = false,
            ValueFromPipelineByPropertyName = true,
            ValueFromRemainingArguments = false,
            HelpMessage = "Only index files that are stripped of source file information")]
        [ValidateNotNullOrEmpty]
        public SwitchParameter PublicSymbolsOnly { get; set; }

        [Parameter(Position = 3,
            Mandatory = true,
            ValueFromPipeline = false,
            ValueFromPipelineByPropertyName = true,
            ValueFromRemainingArguments = false,
            HelpMessage = "A comment to be stored with the transaction")]
        [ValidateNotNullOrEmpty]
        public string Product { get; set; }

        [Parameter(Position = 4,
            Mandatory = false,
            ValueFromPipeline = false,
            ValueFromPipelineByPropertyName = true,
            ValueFromRemainingArguments = false,
            HelpMessage = "A version string to be stored with the transaction")]
        [ValidateNotNullOrEmpty]
        public string Version { get; set; }

        [Parameter(Position = 5,
            Mandatory = false,
            ValueFromPipeline = false,
            ValueFromPipelineByPropertyName = true,
            ValueFromRemainingArguments = false,
            HelpMessage = "A comment to be stored with the transaction")]
        [ValidateNotNullOrEmpty]
        public string Comment { get; set; }

        [Parameter(Position = 6,
            Mandatory = false,
            ValueFromPipeline = false,
            ValueFromPipelineByPropertyName = true,
            ValueFromRemainingArguments = false,
            HelpMessage = "Lock the transaction after creation")]
        [ValidateNotNullOrEmpty]
        public SwitchParameter LockTransaction { get; set; }

        [Parameter(Position = 7,
            Mandatory = false,
            ValueFromPipeline = false,
            ValueFromPipelineByPropertyName = true,
            ValueFromRemainingArguments = false,
            HelpMessage = "Compress the symbols before uploading to the symbol store")]
        [ValidateNotNullOrEmpty]
        public SwitchParameter Compress { get; set; }

        [Parameter(Position = 8,
            Mandatory = true,
            ValueFromPipeline = true,
            ValueFromPipelineByPropertyName = true,
            ValueFromRemainingArguments = false,
            HelpMessage = "The symbol files that should be added to the PSSymbolStore")]
        [ValidateNotNullOrEmpty]
        public string[] SymbolFiles { get; set; }

        protected override bool CreateSymbolStore
        {
            get
            {
                return true;
            }
        }

        private AddTransaction _addTransaction;

        protected override void BeginProcessing()
        {
            base.BeginProcessing();

            _addTransaction = new AddTransaction
                                  {
                                      TransactionId = SymbolStoreObject.GetNextTransactionId(),
                                      Comment = Comment,
                                      FileOrPointer =
                                          (Pointers) ? TransactionUploadType.Pointer : TransactionUploadType.File,
                                      Locked = LockTransaction,
                                      Product = Product,
                                      TransactionDateTime = DateTime.Now,
                                      Version = Version
                                  };
        }

        protected override void ProcessRecord()
        {
            if (SymbolFiles != null)
            {
                foreach (string symbolFile in SymbolFiles)
                {
                    string file = symbolFile;

                    // A negative result means this file's extension isn't one we process
                    if (Helpers.AllowedExtensions.BinarySearch(Path.GetExtension(file), StringComparer.OrdinalIgnoreCase) < 0)
                    {
                        WriteVerbose("Skipping file " + file);
                        continue;
                    }

                    // If the file specified is a relative path, use the current location
                    // to "resolve" the file. This is because our current location
                    // may not be the "working directory" (which will be powershell's launch location)

                    string currentDirectory = SessionState.Path.CurrentFileSystemLocation.Path;
                    if (!Path.IsPathRooted(file))
                    {
                        file = Path.Combine(currentDirectory, file);
                    }

                    if (File.Exists(file))
                    {
                        bool fileAdded = false;
                        Exception exception = null;

                        try
                        {
                            // Add the file to the transaction
                            SymbolFileInformation info = Helpers.GetSymbolFileInformation(file);
                            if (info != null)
                            {
                                // If the symbol file is a PDB,
                                // and doesn't match our public/private requirements...
                                if (info.IsPdb && PublicSymbolsOnly && !info.Public)
                                {
                                    // SKIP IT!
                                    WriteVerbose(file + " contains private symbol information and PublicSymbolsOnly was specified, skipping");
                                    continue;
                                }

                                TransactionFile transactionFile;
                                string serverFileDir = SymbolStoreObject.GetServerFilePath(file, info, out transactionFile);

                                if (!Directory.Exists(serverFileDir))
                                {
                                    Directory.CreateDirectory(serverFileDir);
                                }

                                SymbolType symType;
                                if (info.IsPdb)
                                {
                                    symType = (info.Public) ? SymbolType.PublicSymbols : SymbolType.PrivateSymbols;
                                }
                                else
                                {
                                    symType = SymbolType.Binary;
                                }

                                var refsFile = new RefsFileEntry
                                                   {
                                                       FilePath = file,
                                                       IsCompressed = Compress,
                                                       TransactionId = _addTransaction.TransactionId,
                                                       TransactionUploadType =
                                                           (Pointers)
                                                               ? TransactionUploadType.Pointer
                                                               : TransactionUploadType.File,
                                                       SymbolType = symType
                                                   };

                                // If we're not storing a pointer to this file, copy it up
                                if (!Pointers)
                                {
                                    fileAdded = FileCopier.Copy(this, file, Path.Combine(serverFileDir, Path.GetFileName(file)), Compress);
                                }
                                else
                                {
                                    // Update the file.ptr file to point to the new file
                                    File.WriteAllText(Path.Combine(serverFileDir, PSSymbolStore.SymbolStore.FILE_PTR), file);
                                }

                                if (fileAdded)
                                {
                                    // Write out the refs file entry
                                    Helpers.AppendLinesToFile(Path.Combine(serverFileDir, PSSymbolStore.SymbolStore.REFS_PTR), refsFile.ToString());

                                    _addTransaction.Files.Add(transactionFile);

                                    WriteVerbose(file + " uploaded");
                                }
                                else
                                {
                                    WriteError(new ErrorRecord(new Exception(), "Symbol file not added: " + file, ErrorCategory.WriteError, file));
                                }
                            }
                        }
                        catch (SystemException sysEx)
                        {
                            exception = sysEx;
                        }

                        if (!fileAdded)
                        {
                            WriteError(new ErrorRecord(exception, "Error adding symbol file", ErrorCategory.WriteError, file));
                            if (!ShouldContinue("Adding symbol file " + file + " in a transaction failed, continue adding transaction?", "Error Creating Transaction"))
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        WriteError(new ErrorRecord(new Exception(), "Symbol file not found", ErrorCategory.InvalidArgument, file));
                    }
                }
            }
        }

        protected override void EndProcessing()
        {
            // Close out and commit the "add" transaction
            SymbolStoreObject.CommitTransaction(_addTransaction);

            base.EndProcessing();
        }
    }
}
